package com.ddw.wotcg.server.utils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.CompositeFilter;
import com.google.appengine.api.datastore.Query.CompositeFilterOperator;
import com.google.appengine.api.datastore.Query.Filter;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.FilterPredicate;

/**
 * Represents a helper class for accessing the user data
 * 
 * @author fungus1487
 * 
 */
public abstract class UserHelper {
	
	private static final String KIND = "User";
	private static final String USERNAME = "Username";
	private static final String EMAIL = "Email";
	private static final String PASSWORD = "Password";
	private static final String CREATED = "Created";
	private static final String SESSION = "Session";
	private static final String SESSION_CREATED = "SessionCreated";
	private static final int ONE_DAY = 1000 * 60 * 60 * 24;
	
	/**
	 * Converts an entity into a user instance
	 * 
	 * @param entity
	 *            the entity to read values from
	 * @return a user instance of the entity or null if not found
	 */
	private static User asUser(Entity entity) {
		// Check we have a db record
		if (entity == null) {
			return null;
		}
		
		// Get default user details
		final User user = new User();
		user.username = (String) entity.getProperty(USERNAME);
		user.email = (String) entity.getProperty(EMAIL);
		user.created = (Date) entity.getProperty(CREATED);
		user.password = (String) entity.getProperty(PASSWORD);
		
		// Add session if possible
		final Object sessionId = entity.getProperty(SESSION);
		if (sessionId != null && entity.hasProperty(SESSION_CREATED)) {
			user.session = UUID.fromString((String) sessionId);
			user.sessionCreated = (Date) entity.getProperty(SESSION_CREATED);
		}
		
		return user;
	}
	
	/**
	 * Finds a user by their email address
	 * 
	 * @param email
	 *            the email to search for
	 * @return a user or null if search failed
	 */
	public static User byEmail(String email) {
		final DatastoreService ds = DatastoreServiceFactory
				.getDatastoreService();
		return asUser(ds.prepare(
				new Query(KIND).setFilter(new FilterPredicate(EMAIL,
						FilterOperator.EQUAL, email))).asSingleEntity());
	}
	
	/**
	 * Finds a user by their session id
	 * 
	 * @param session
	 *            the session id to find by
	 * @return a user instance or null if not found or out of date session
	 */
	public static User bySession(UUID session) {
		if (session == null) {
			return null;
		}
		
		final DatastoreService ds = DatastoreServiceFactory
				.getDatastoreService();
		
		// Create filter based on session id and maximum session time
		final List<Filter> filters = new ArrayList<Filter>(2);
		filters.add(new FilterPredicate(SESSION, FilterOperator.EQUAL, session
				.toString()));
		filters.add(new FilterPredicate(SESSION_CREATED,
				FilterOperator.GREATER_THAN_OR_EQUAL, new Date(System
						.currentTimeMillis() - ONE_DAY)));
		
		return asUser(ds.prepare(
				new Query(KIND).setFilter(new CompositeFilter(
						CompositeFilterOperator.AND, filters)))
				.asSingleEntity());
	}
	
	/**
	 * Finds a user by their username
	 * 
	 * @param username
	 *            the username to find
	 * @return a user or null if not found
	 */
	public static User byUsername(String username) {
		final DatastoreService ds = DatastoreServiceFactory
				.getDatastoreService();
		return asUser(ds.prepare(
				new Query(KIND).setFilter(new FilterPredicate(USERNAME,
						FilterOperator.EQUAL, username))).asSingleEntity());
	}
	
	/**
	 * Saves and updates a user, can be used to set the users new session
	 * information
	 * 
	 * @param user
	 *            the user to save
	 */
	public static void save(User user) {
		final DatastoreService ds = DatastoreServiceFactory
				.getDatastoreService();
		final Entity entity = new Entity(KIND, user.email);
		entity.setProperty(EMAIL, user.email);
		entity.setProperty(USERNAME, user.username);
		entity.setProperty(PASSWORD, user.password);
		entity.setProperty(CREATED, user.created);
		entity.setProperty(SESSION,
				user.session != null ? user.session.toString() : null);
		entity.setProperty(SESSION_CREATED, user.sessionCreated);
		ds.put(entity);
	}
}
